package com.tssup.fesco.common.serializer.protostuff;

import io.protostuff.*;
import io.protostuff.runtime.RuntimeSchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by haomin on 2017/3/24.
 * ProtoStuff 序列化工具
 */
public class ProtoStuffSerializerUtil {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();

    @Autowired
    // 目的是复用 LinkedBuffer 中链表头结点 byte[]
    private static final InternalThreadLocal<LinkedBuffer> bufThreadLocal = new InternalThreadLocal<LinkedBuffer>() {
        @Override
        protected LinkedBuffer initialValue() {
            return LinkedBuffer.allocate(512);
        }
    };

    /**
     * 获取Schema , 已生成的Schema保存起来
     * @param clazz
     * @param <T>
     * @return
     */
    private static <T> Schema<T> getSchema(Class<T> clazz) {
        @SuppressWarnings("unchecked")
        Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
        if (schema == null) {
            schema = RuntimeSchema.getSchema(clazz);
            if (schema != null) {
                cachedSchema.put(clazz, schema);
            }
        }
        return schema;
    }

    /**
     * 一般对象的序列化
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> byte[] serialize(T obj) {
        if (obj == null) {
            throw new RuntimeException("序列化对象(" + obj + ")!");
        }
        @SuppressWarnings("unchecked")
        LinkedBuffer buffer = bufThreadLocal.get();
        try {
            return ProtostuffIOUtil.toByteArray(obj, getSchema((Class<T>) obj.getClass()), buffer);
        } catch (Exception e) {
            throw new RuntimeException("序列化(" + obj.getClass() + ")对象(" + obj + ")发生异常!", e);
        } finally {
            buffer.clear();
        }
    }

    /**
     * 一般对象的反序列化
     * @param paramArrayOfByte
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T> T deserialize(byte[] paramArrayOfByte, Class<T> targetClass) {
        if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
            throw new RuntimeException("反序列化对象发生异常,byte序列为空!");
        }
        T instance = null;
        try {
            instance = targetClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException("反序列化过程中依据类型创建对象失败!", e);
        }
        ProtostuffIOUtil.mergeFrom(paramArrayOfByte, instance, getSchema(targetClass));
        return instance;
    }

    /**
     * Collection类型对象的序列化，包括各种list、set、queue
     * @param collection
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T> byte[] serializeCollection(Collection<T> collection, Class<T> targetClass) {
        if (collection == null || collection.isEmpty()) {
            throw new RuntimeException("序列化Collection类型对象集合(" + collection + ")参数异常!");
        }
        LinkedBuffer buffer = bufThreadLocal.get();
        ProtostuffOutput bos = new ProtostuffOutput(buffer);
        LinkedBuffer bufferInput = bufThreadLocal.get();
        try {
            MessageCollectionSchema<T> messageCollectionSchema = new MessageCollectionSchema<T>(getSchema(targetClass));
            messageCollectionSchema.writeTo(bos, collection);
            return bos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("序列化Collection类型对象集合(" + collection + ")发生异常!", e);
        } finally {
            buffer.clear();
            bos.clear();
        }
    }

    /**
     * Collection类型对象的反序列化，各种list、set、queue
     *
     * @param paramArrayOfByte
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T> Collection<T> deSerializeCollection(byte[] paramArrayOfByte, Class<T> targetClass, Class<? extends Collection<T>> collectionClass) {
        if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
            throw new RuntimeException("反序列化Collection类型对象发生异常,byte序列为空!");
        }
        try {
            MessageCollectionSchema<T> messageCollectionSchema = new MessageCollectionSchema<T>(getSchema(targetClass));
            ByteArrayInput bis = new ByteArrayInput(paramArrayOfByte, 0, paramArrayOfByte.length, true);
//            Collection<T> collection = messageCollectionSchema.newMessage();
            Collection<T> collection = CollectionSchema.MessageFactories.getFactory(collectionClass).newMessage();
            messageCollectionSchema.mergeFrom(bis, collection);
            return collection;
        } catch (Exception e) {
            throw new RuntimeException("反序列化Collection类型对象集合发生异常!", e);
        }
    }

    /**
     * Map类型对象的序列化
     * @param map
     * @param keyClass
     * @param valusClass
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> byte[] serializeMap(Map<K, V> map, Class<K> keyClass, Class<V> valusClass) {
        if (map == null || map.isEmpty()) {
            throw new RuntimeException("序列化Map类型对象(" + map + ")参数异常!");
        }
        LinkedBuffer buffer = bufThreadLocal.get();
        ProtostuffOutput bos = new ProtostuffOutput(buffer);
        LinkedBuffer bufferInput = bufThreadLocal.get();
        try {
            MessageMapSchema<K, V> messageMapSchema = new MessageMapSchema<K, V>(getSchema(keyClass), getSchema(valusClass));
            messageMapSchema.writeTo(bos, map);
            return bos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("序列化Map类型对象(" + map + ")发生异常!", e);
        } finally {
            buffer.clear();
            bos.clear();
        }
    }

    /**
     * Map类型对象的反序列化
     * @param paramArrayOfByte
     * @param keyClass
     * @param valusClass
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> Map<K, V> deSerializeMap(byte[] paramArrayOfByte, Class<K> keyClass, Class<V> valusClass, Class<? extends Map<?, ?>> mapClass) {
        if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
            throw new RuntimeException("反序列化Map类型对象发生异常,byte序列为空!");
        }
        try {
            MessageMapSchema<K, V> messageMapSchema = new MessageMapSchema<K, V>(getSchema(keyClass), getSchema(valusClass));
            ByteArrayInput bis = new ByteArrayInput(paramArrayOfByte, 0, paramArrayOfByte.length, true);
//            Map<K, V> map = messageMapSchema.newMessage();
            Map<K, V>  map = MessageMapSchema.MessageFactories.getFactory(mapClass).newMessage();
            messageMapSchema.mergeFrom(bis, map);
            return map;
        } catch (Exception e) {
            throw new RuntimeException("反序列化Map类型对象集合发生异常!", e);
        }
    }
}
